package net.lunar.xml.convertor;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;

import net.lunar.xml.configeration.MessageProperties;

@Component
public class XMLConvertor {

	@Autowired
	private MessageProperties messageProperties;
	
	private Map<String, JAXBContext> contextMap;
	
	private JAXBContext getContext(Class clazz) {
		if(contextMap == null) {
			contextMap = new HashMap<String, JAXBContext>();
		}
		
		if (contextMap.containsKey(clazz.getName())) {
			return contextMap.get(clazz.getName());
		} else {
			try {
				JAXBContext context = JAXBContext.newInstance(clazz);
				contextMap.put(clazz.getName(), context);
				return context;
			} catch (JAXBException e) {
				e.printStackTrace();
				return null;
			}
		}
	}

	public String toXML(Object obj) {
		return toXML(obj, messageProperties.isFormatted(), messageProperties.isIgnoreHead());
	}

	public String toXML(Object obj, boolean formatted, boolean ignoreHead) {
		String result = null;
		try {
			JAXBContext context = this.getContext(obj.getClass());
			Marshaller marshaller = context.createMarshaller();

			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formatted);
			marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, ignoreHead);

			marshaller.setProperty("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
					new CharacterEscapeHandler() {
						@Override
						public void escape(char[] ch, int start, int length, boolean isAttVal, Writer writer)
								throws IOException {
							writer.write(ch, start, length);
						}
					});

			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);

			result = writer.toString();
		} catch (JAXBException e) {
			e.printStackTrace();
		}

		return result;
	}

	public <T> T toObject(String xml, Class<T> clazz) {

		try {
			JAXBContext context = this.getContext(clazz);
			Unmarshaller u = context.createUnmarshaller();
			return (T) u.unmarshal(new StringReader(xml));
		} catch (JAXBException e) {
			e.printStackTrace();
		}

		return null;
	}
	
	public void clear() {
		if(this.contextMap != null) {
			this.contextMap.clear();
		}
	}
}
